#include "CMD5.h"
#include <string.h>
#include <stdio.h>

#define F(x,y,z)	( ((x) & (y)) | ((~(x)) & (z)) )
#define G(x,y,z)	( ((x) & (z)) | ((~(z)) & (y)) )
#define H(x,y,z)	( (x) ^ (y) ^ (z) )
#define I(x,y,z)	( (y) ^ ( (x) | ~(z) ) )

#define rol(x,y)	( ((x) << (y)) | (((kx_u32)x) >> (32-y)) )

#define subround(f,w,x,y,z,k,s,ti)	w = x + rol(w + f(x,y,z) + aBlock[k] + ti, s)

kx_void MD5ComputeBlock(MD5_CTX* mdContext, kx_u32* aBlock);
kx_void MD5Init(MD5_CTX* mdContext)
{
	if (mdContext == KX_NULL) return;
	mdContext->iH[0] = 0x67452301;
	mdContext->iH[1] = 0xefcdab89;
	mdContext->iH[2] = 0x98badcfe;
	mdContext->iH[3] = 0x10325476;
	mdContext->iBlockUsed = 0;
	mdContext->iLength = 0;
}

kx_void MD5Update(MD5_CTX* mdContext, const kx_s8 *aBuffer, kx_s32 aLength)
{
	if (mdContext == KX_NULL || aBuffer == KX_NULL) return;
	// Update the length field.
	mdContext->iLength += aLength;

	// We must complete and process at least one block if exceed block size.
	while (mdContext->iBlockUsed + aLength >= (kx_s32)sizeof(mdContext->iBlock)) 
	{
		kx_u32 wordblock[16];
		kx_s32 i;
		memcpy(mdContext->iBlock + mdContext->iBlockUsed, aBuffer, sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		aBuffer += (sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		aLength -= (sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		// Now process the block. Gather bytes little-endian into words
		for (i = 0; i < 16; i++) 
		{
			wordblock[i] = 
				(((kx_u32) mdContext->iBlock[i * 4 + 3]) << 24) |
				(((kx_u32) mdContext->iBlock[i * 4 + 2]) << 16) |
				(((kx_u32) mdContext->iBlock[i * 4 + 1]) << 8) |
				(((kx_u32) mdContext->iBlock[i * 4 + 0]) << 0);
		}
		MD5ComputeBlock(mdContext, wordblock);
		mdContext->iBlockUsed = 0;
	}
	// Trivial case: just add to the block.
	memcpy(mdContext->iBlock + mdContext->iBlockUsed, aBuffer, aLength);
	mdContext->iBlockUsed += aLength;
}

kx_void MD5Final(MD5_CTX* mdContext)
{
	if (mdContext == KX_NULL)
		return;
	else
	{
	kx_s32 i;
    kx_u32 lenhi = (mdContext->iLength >> (32 - 3));
    kx_u32 lenlo = (mdContext->iLength << 3);

    kx_u32 pad;
    kx_s8 c[64];
    if (mdContext->iBlockUsed >= 56) pad = 56 + sizeof(mdContext->iBlock) - mdContext->iBlockUsed;
    else pad = 56 - mdContext->iBlockUsed;

    memset(c, 0, pad);
    c[0] = (kx_s8) 0x80;
    MD5Update(mdContext, c, pad);

    c[7] = (kx_s8) (lenhi >> 24);
    c[6] = (kx_s8) (lenhi >> 16);
    c[5] = (kx_s8) (lenhi >> 8);
    c[4] = (kx_s8) (lenhi >> 0);
    c[3] = (kx_s8) (lenlo >> 24);
    c[2] = (kx_s8) (lenlo >> 16);
    c[1] = (kx_s8) (lenlo >> 8);
    c[0] = (kx_s8) (lenlo >> 0);
    MD5Update(mdContext, c, 8);

    for (i = 0; i < 4; i++) 
	{
		mdContext->digest[4 * i + 3] = (kx_s8) (mdContext->iH[i] >> 24);
		mdContext->digest[4 * i + 2] = (kx_s8) (mdContext->iH[i] >> 16);
		mdContext->digest[4 * i + 1] = (kx_s8) (mdContext->iH[i] >> 8);
		mdContext->digest[4 * i + 0] = (kx_s8) (mdContext->iH[i] >> 0);
    }
	}
}

kx_void MD5ComputeBlock(MD5_CTX* mdContext, kx_u32* aBlock)
{
	kx_u32 a = mdContext->iH[0];
	kx_u32 b = mdContext->iH[1];
	kx_u32 c = mdContext->iH[2];
	kx_u32 d = mdContext->iH[3];

	subround(F, a, b, c, d, 0, 7, 0xd76aa478);
	subround(F, d, a, b, c, 1, 12, 0xe8c7b756);
	subround(F, c, d, a, b, 2, 17, 0x242070db);
	subround(F, b, c, d, a, 3, 22, 0xc1bdceee);
	subround(F, a, b, c, d, 4, 7, 0xf57c0faf);
	subround(F, d, a, b, c, 5, 12, 0x4787c62a);
	subround(F, c, d, a, b, 6, 17, 0xa8304613);
	subround(F, b, c, d, a, 7, 22, 0xfd469501);
	subround(F, a, b, c, d, 8, 7, 0x698098d8);
	subround(F, d, a, b, c, 9, 12, 0x8b44f7af);
	subround(F, c, d, a, b, 10, 17, 0xffff5bb1);
	subround(F, b, c, d, a, 11, 22, 0x895cd7be);
	subround(F, a, b, c, d, 12, 7, 0x6b901122);
	subround(F, d, a, b, c, 13, 12, 0xfd987193);
	subround(F, c, d, a, b, 14, 17, 0xa679438e);
	subround(F, b, c, d, a, 15, 22, 0x49b40821);
	subround(G, a, b, c, d, 1, 5, 0xf61e2562);
	subround(G, d, a, b, c, 6, 9, 0xc040b340);
	subround(G, c, d, a, b, 11, 14, 0x265e5a51);
	subround(G, b, c, d, a, 0, 20, 0xe9b6c7aa);
	subround(G, a, b, c, d, 5, 5, 0xd62f105d);
	subround(G, d, a, b, c, 10, 9, 0x02441453);
	subround(G, c, d, a, b, 15, 14, 0xd8a1e681);
	subround(G, b, c, d, a, 4, 20, 0xe7d3fbc8);
	subround(G, a, b, c, d, 9, 5, 0x21e1cde6);
	subround(G, d, a, b, c, 14, 9, 0xc33707d6);
	subround(G, c, d, a, b, 3, 14, 0xf4d50d87);
	subround(G, b, c, d, a, 8, 20, 0x455a14ed);
	subround(G, a, b, c, d, 13, 5, 0xa9e3e905);
	subround(G, d, a, b, c, 2, 9, 0xfcefa3f8);
	subround(G, c, d, a, b, 7, 14, 0x676f02d9);
	subround(G, b, c, d, a, 12, 20, 0x8d2a4c8a);
	subround(H, a, b, c, d, 5, 4, 0xfffa3942);
	subround(H, d, a, b, c, 8, 11, 0x8771f681);
	subround(H, c, d, a, b, 11, 16, 0x6d9d6122);
	subround(H, b, c, d, a, 14, 23, 0xfde5380c);
	subround(H, a, b, c, d, 1, 4, 0xa4beea44);
	subround(H, d, a, b, c, 4, 11, 0x4bdecfa9);
	subround(H, c, d, a, b, 7, 16, 0xf6bb4b60);
	subround(H, b, c, d, a, 10, 23, 0xbebfbc70);
	subround(H, a, b, c, d, 13, 4, 0x289b7ec6);
	subround(H, d, a, b, c, 0, 11, 0xeaa127fa);
	subround(H, c, d, a, b, 3, 16, 0xd4ef3085);
	subround(H, b, c, d, a, 6, 23, 0x04881d05);
	subround(H, a, b, c, d, 9, 4, 0xd9d4d039);
	subround(H, d, a, b, c, 12, 11, 0xe6db99e5);
	subround(H, c, d, a, b, 15, 16, 0x1fa27cf8);
	subround(H, b, c, d, a, 2, 23, 0xc4ac5665);
	subround(I, a, b, c, d, 0, 6, 0xf4292244);
	subround(I, d, a, b, c, 7, 10, 0x432aff97);
	subround(I, c, d, a, b, 14, 15, 0xab9423a7);
	subround(I, b, c, d, a, 5, 21, 0xfc93a039);
	subround(I, a, b, c, d, 12, 6, 0x655b59c3);
	subround(I, d, a, b, c, 3, 10, 0x8f0ccc92);
	subround(I, c, d, a, b, 10, 15, 0xffeff47d);
	subround(I, b, c, d, a, 1, 21, 0x85845dd1);
	subround(I, a, b, c, d, 8, 6, 0x6fa87e4f);
	subround(I, d, a, b, c, 15, 10, 0xfe2ce6e0);
	subround(I, c, d, a, b, 6, 15, 0xa3014314);
	subround(I, b, c, d, a, 13, 21, 0x4e0811a1);
	subround(I, a, b, c, d, 4, 6, 0xf7537e82);
	subround(I, d, a, b, c, 11, 10, 0xbd3af235);
	subround(I, c, d, a, b, 2, 15, 0x2ad7d2bb);
	subround(I, b, c, d, a, 9, 21, 0xeb86d391);

	mdContext->iH[0] += a;
	mdContext->iH[1] += b;
	mdContext->iH[2] += c;
	mdContext->iH[3] += d;
}

kx_void MD5UpdateStream(MD5_CTX* mdContext, MD5_STREAM* stream)
{
	if (mdContext == KX_NULL || stream == KX_NULL) return;
	else
	{
	kx_s32 aLength = stream->len;
	// Update the length field.
	mdContext->iLength += aLength;
	
	// We must complete and process at least one block if exceed block size.
	while (mdContext->iBlockUsed + aLength >= (kx_s32)sizeof(mdContext->iBlock)) 
	{
		kx_u32 wordblock[16];
		kx_s32 i;
		(*stream->read)(stream, mdContext->iBlock + mdContext->iBlockUsed, sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		//kx_memcpy(mdContext->iBlock + mdContext->iBlockUsed, aBuffer, sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		//aBuffer += (sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		aLength -= (sizeof(mdContext->iBlock) - mdContext->iBlockUsed);
		// Now process the block. Gather bytes little-endian into words
		for (i = 0; i < 16; i++) 
		{
			wordblock[i] = 
			(((kx_u32) mdContext->iBlock[i * 4 + 3]) << 24) |
			(((kx_u32) mdContext->iBlock[i * 4 + 2]) << 16) |
			(((kx_u32) mdContext->iBlock[i * 4 + 1]) << 8) |
			(((kx_u32) mdContext->iBlock[i * 4 + 0]) << 0);
		}
		MD5ComputeBlock(mdContext, wordblock);
		mdContext->iBlockUsed = 0;
	}
	// Trivial case: just add to the block.
	(*stream->read)(stream, mdContext->iBlock + mdContext->iBlockUsed, aLength);
	//kx_memcpy(mdContext->iBlock + mdContext->iBlockUsed, aBuffer, aLength);
	mdContext->iBlockUsed += aLength;
	}
}


kx_bool md5_encode_str(kx_s8* buffer, const kx_s8 *string) 
{
	MD5_CTX mdContext;
	kx_s32 i = 0;
	
	MD5Init(&mdContext);
	MD5Update(&mdContext,string, strlen(string));
	MD5Final(&mdContext);
	for (i = 0; i < 16; i++)
	{
		sprintf(buffer, "%02x", (kx_u8)mdContext.digest[i]);
		buffer += 2;
	}
	buffer[0] = 0;
	return KX_TRUE;
}

kx_bool md5_encode_stream_str(kx_s8* buffer, MD5_STREAM *string) 
{	
	MD5_CTX mdContext;
	kx_s32 i = 0;
	
	MD5Init(&mdContext);
	MD5UpdateStream(&mdContext, string);
	MD5Final(&mdContext);
	for (i = 0; i < 16; i++)
	{
		sprintf(buffer, "%02x", (kx_u8)mdContext.digest[i]);
		buffer += 2;
	}
	buffer[0] = 0;
	return KX_TRUE;
}

kx_bool md5_encode_bin(kx_u8* buffer, const kx_u8 *bin, kx_s32 len)
{
	MD5_CTX mdContext;
	
	MD5Init(&mdContext);
	MD5Update(&mdContext, (kx_s8*)bin, len);
	MD5Final(&mdContext);
	memcpy(buffer, mdContext.digest, 16);
	return KX_TRUE;
}

kx_bool md5_encode_str_bin(kx_s8* buffer, const kx_u8 *bin, kx_s32 len) 
{
	MD5_CTX mdContext;
	kx_s32 i = 0;
	
	MD5Init(&mdContext);
	MD5Update(&mdContext,(kx_s8*)bin,len);
	MD5Final(&mdContext);
	for (i = 0; i < 16; i++)
	{
		sprintf(buffer, "%02x", (kx_u8)mdContext.digest[i]);
		buffer += 2;
	}
	buffer[0] = 0;
	return KX_TRUE;
}

kx_bool md5_encode_stream_bin(kx_u8* buffer, MD5_STREAM *string)
{
	MD5_CTX mdContext;
	
	MD5Init(&mdContext);
	MD5UpdateStream(&mdContext, string);
	MD5Final(&mdContext);
	memcpy(buffer, mdContext.digest, 16);
	return KX_TRUE;
}

//one impelement of MD5Stream
kx_void kx_md5_stream_read(MD5_STREAM* stream, kx_void* buffer, kx_s32 len)
{
	KX_MD5_STREAM1* str = (KX_MD5_STREAM1*)stream;
	while (len > 0)
	{
		if (str->index_arg >= str->argc)
		{
			return;
		}
		else
		{
			if (len >= str->arg_len[str->index_arg] - str->pos_arg)
			{
				memcpy(buffer, str->argv[str->index_arg]+str->pos_arg, str->arg_len[str->index_arg] - str->pos_arg);
				
				buffer = (kx_s32*)((int)buffer + (str->arg_len[str->index_arg] - str->pos_arg));
				len -= str->arg_len[str->index_arg] - str->pos_arg;
				str->pos_arg = 0;
				str->index_arg++;
			}
			else
			{
				memcpy(buffer, str->argv[str->index_arg]+str->pos_arg, len);
				
				buffer = (kx_s32*)((kx_s32)buffer + len);
				str->pos_arg += len;
				len = 0;
			}
		}
	}
}